/************** Zhihui *****************/

#include "Move.h"
#include "Route.h"
#include "contest_dm.h"
#include <iostream>
#include <typeinfo>
#include <unordered_map>
#include <chrono>

namespace ieda_contest {

// iEDA的instance不需要考虑blockage，但是需要先判断一下Gcell里面的instance总面积是否overflow。
// 传入网格坐标。
bool Move::addCell(idb::IdbInstance *CellInst, int Row, int Col) {
    bool Success = true;
    int single_gcell_area = _data_manager->get_database()->get_single_gcell_area();
    int single_gcell_x_span = _data_manager->get_database()->get_single_gcell_x_span();
    int single_gcell_y_span = _data_manager->get_database()->get_single_gcell_y_span();

    std::map<ContestCoord, int, CmpContestCoord>& contest_coord_used_area_map = _data_manager->get_database()->get_coord_used_area_map();
    ContestCoord grid_coord(Row, Col, 0);

    int idb_instance_area = CellInst->get_cell_master()->get_width() * CellInst->get_cell_master()->get_height();
    
    if ((contest_coord_used_area_map[grid_coord] + idb_instance_area) > single_gcell_area){
      std::cout << "Overlap GCell: " << grid_coord.get_x() << grid_coord.get_y() << grid_coord.get_layer_idx() << std::endl;
      Success = false;
      return Success;
    } else {
      contest_coord_used_area_map[grid_coord] += idb_instance_area;
      CellInst->set_coodinate(grid_coord.get_x() * single_gcell_x_span + 1, grid_coord.get_y() * single_gcell_y_span + 1, true);
    }

    return Success;
}

// iEDA的instance不需要考虑blockage，但是需要先判断一下Gcell里面的instance总面积是否overflow。
void Move::removeCell(idb::IdbInstance *CellInst) {
    std::map<ContestCoord, int, CmpContestCoord>& contest_coord_used_area_map = _data_manager->get_database()->get_coord_used_area_map();

    int Row = CellInst->get_coordinate()->get_x();
    int Col =  CellInst->get_coordinate()->get_y();
    // 真实坐标转为Gcell坐标，用myContestEvaluation::getGCellCoord会出问题。
    int single_gcell_x_span = _data_manager->get_database()->get_single_gcell_x_span();
    int single_gcell_y_span = _data_manager->get_database()->get_single_gcell_y_span();
    Row = (Row > 0 ? Row - 1 : Row) / single_gcell_x_span;
    Col = (Col > 0 ? Col - 1 : Col) / single_gcell_y_span;

    ContestCoord grid_coord(Row, Col, 0);

    int idb_instance_area = CellInst->get_cell_master()->get_width() * CellInst->get_cell_master()->get_height();
    contest_coord_used_area_map[grid_coord] -= idb_instance_area;
}

// 需要用到Router，在指定位置添加一个 CellInst 并进行路由。
// 传入网格坐标。
std::pair<long long, bool> Move::add_and_route(idb::IdbInstance *CellPtr, int Row, int Col, bool virtual_route) {
    bool Success = addCell(CellPtr, Row, Col); // 调用 addCell 将指定的 CellInst 添加到指定的行（Row）和列（Col）位置。
    if (Success == false) {
        return {INT_MAX, false};
    }
    Route Route_do(_data_manager);
    std::vector<std::pair<ContestNet *, std::vector<ContestSegment> *> >  OriginRoutes; // 路由失败，用来还原的。
    bool Accept = true;
    long long final_cost_sum = 0;
    int Net_index = 0;
    for (auto NetPtr : _data_manager->get_database()->getRelativeNetsMap().at(CellPtr)) {
        auto &OriginRoute = NetPtr->get_routing_segment_list();
        auto Pair = Route_do.singleNetRoute(NetPtr, OriginRoute);

        ///////////// 一个测试 检测原始布线和新布线的坐标有没有变化 /////////////////////
        // std::cout << NetPtr->get_net_name() << std::endl;
        // std::cout << "Current Net Number: " << Net_index << std::endl;
        // Net_index++;
        // std::cout << "Number of Routing Segment: " << OriginRoute.size() << std::endl;
        // std::cout << "Number of Pin: " << NetPtr->get_pin_list().size() << std::endl;

        // int originSegment_index = 0;
        // for (ContestSegment originSegment : OriginRoute) {
        //     ContestCoord coord_start = originSegment.get_first();
        //     ContestCoord coord_end = originSegment.get_second();
        //     std::cout << "Current originSegment Number: " << originSegment_index << std::endl;
        //     originSegment_index++;
        //     std::cout << "Start Points of Original Routing Segment: x = " << coord_start.get_x() 
        //     << ", y = " << coord_start.get_y() << ", L = " << coord_start.get_layer_idx() << std::endl;
        //     std::cout << "End Points of Original Routing Segment: x = " << coord_end.get_x() 
        //     << ", y = " << coord_end.get_y() << ", L = " << coord_end.get_layer_idx() << std::endl;
        // }

        // std::cout << "---------------print Old and new route-----------------" << std::endl;

        // int newSegment_index = 0;
        // std::cout << "Number of Routing Segment: " << Pair.first.size() << std::endl;
        // for (ContestSegment newSegment : Pair.first) {
        //     ContestCoord coord_start = newSegment.get_first();
        //     ContestCoord coord_end = newSegment.get_second();
        //     std::cout << "Current newSegment Number: " << newSegment_index << std::endl;
        //     newSegment_index++;
        //     std::cout << "Start Points of New Routing Segment: x = " << coord_start.get_x() 
        //     << ", y = " << coord_start.get_y() << ", L = " << coord_start.get_layer_idx() << std::endl;
        //     std::cout << "End Points of New Routing Segment: x = " << coord_end.get_x() 
        //     << ", y = " << coord_end.get_y() << ", L = " << coord_end.get_layer_idx() << std::endl;
        // }
        ////////////////// 测试完成 ///////////////////////////////////

        OriginRoutes.emplace_back(NetPtr, std::move(&OriginRoute));

        // 如果路由失败（Pair.second == false），将 Accept 设为 false 并跳出循环。
        if (Pair.second == false) {
            Accept = false;
            break;
        }

        int routingCost_1 = NetPtr->get_net_length(); // Figure out cost of original routing
        int routingCost_2 = Route_do.get_newCost(Pair.first); // Figure out cost of new routing
        final_cost_sum += routingCost_2 - routingCost_1;

        // Route_do.reduceRouteSegments(Pair.first);

        OriginRoute = std::move(Pair.first);
        NetPtr->set_net_length(routingCost_2);

        Route_do.addNet(NetPtr);
    }

    if (virtual_route == false) {
        if(Accept) {
            if (final_cost_sum < 0) {
                std::cout << "Route Accept" << std::endl;
                Route_do.set_currentCost(final_cost_sum);
                _data_manager->get_database()->setMoveCost(final_cost_sum);
                return {final_cost_sum, true};
            } else {
                // std::cout << "Route Reject" << std::endl;
                // 如果Accept 为 true，说明路由成功，但是如果新布线方案让结果变差了也要将原始路由信息还原，从最后一个网络开始，逐一从网格中移除网络并还原原始路由信息。
                while (OriginRoutes.size()) {
                    Route_do.removeNet(OriginRoutes.back().first);
                    Route_do.getNetMap()[OriginRoutes.back().first->get_net_name()]->get_routing_segment_list() = std::move(*OriginRoutes.back().second);
                    OriginRoutes.back().first->set_net_length(Route_do.get_newCost(*OriginRoutes.back().second));
                    OriginRoutes.pop_back();
                }
                return {final_cost_sum, false};
            }
        }
    }

    // 如果Accept 为 false，说明路由失败，将原始路由信息还原，从最后一个网络开始，逐一从网格中移除网络并还原原始路由信息。
    Route_do.getNetMap()[OriginRoutes.back().first->get_net_name()]->get_routing_segment_list() = std::move(*OriginRoutes.back().second); // 等式前后本来就是一样的，这一步可不要。
    OriginRoutes.pop_back();
    while (OriginRoutes.size()) {
        Route_do.removeNet(OriginRoutes.back().first);
        Route_do.getNetMap()[OriginRoutes.back().first->get_net_name()]->get_routing_segment_list() = std::move(*OriginRoutes.back().second);
        OriginRoutes.back().first->set_net_length(Route_do.get_newCost(*OriginRoutes.back().second));
        OriginRoutes.pop_back();
    }
    removeCell(CellPtr);
    if(virtual_route == true && Accept == false) return {INT_MAX, Accept};
    return {final_cost_sum, Accept};
}

void Move::move(RegionCalculator &RC, std::chrono::time_point<std::chrono::_V2::system_clock, 
std::chrono::duration<long int, std::ratio<1, 1000000000> > > startTime) {
    
    std::vector<std::pair<long long, idb::IdbInstance *>> CellNetLength;
    // 1. Sort Cells for the order of cell movement (higher net cost first)
    for (auto CellPtr : _data_manager->get_idb_design()->get_instance_list()->get_instance_list()) {
        long long NetLength = 0;
        for (auto NetPtr : _data_manager->get_database()->getRelativeNetsMap().at(CellPtr)) {
            // 1.1 Cost of Cell Order, defined as its connected NetLength
            NetLength += NetPtr->get_net_length();
        }
        CellNetLength.emplace_back(NetLength, CellPtr);
    }
    // 1.2 Sort Cells by NetLength (Higher Net Cost implies Critical Cell to Move First)
    std::sort(CellNetLength.begin(), CellNetLength.end(), std::greater<std::pair<long long, idb::IdbInstance *>>());
    unsigned MoveCnt = 0;
    unsigned tryMoveCnt = 0;

    // 2. Constructing Candidate Position (in sorted order)
    for (auto &P : CellNetLength) {
        auto CellPtr = P.second;
        int RowBeginIdx = 0, RowEndIdx = 0, ColBeginIdx = 0, ColEndIdx = 0;
        // 2.1 Get this cell's optimal region (median of each connected nets)
        std::tie(RowBeginIdx, RowEndIdx, ColBeginIdx, ColEndIdx) = RC.getRegion(CellPtr); 
        // 参考代码跳转到OptimalRegion::getRegion，理论上要用FinalRegion::getRegion。
        std::vector<std::pair<int, int>> CandidatePos;
        
        // 2.2 Consider the case if this cell has Voltage Constraint
        // None

        // 2.3 Consider the case if Cell does not have Voltage Constraint
        for (int R = RowBeginIdx; R <= RowEndIdx; ++R) {
            for (int C = ColBeginIdx; C <= ColEndIdx; ++C) {
                CandidatePos.emplace_back(R, C);
            }
        }

        // 2.4 use FLUTE to estimate WL for situation after cell moved to each candidate position
        std::map<std::pair<int, int>, long long> Grid_to_WL;
        for (auto P : CandidatePos) {
            long long total_WL = 0;
            // 2.4.1 for all connected net
            for (auto NetPtr : _data_manager->get_database()->getRelativeNetsMap().at(CellPtr)){
                std::vector<int> X;
                std::vector<int> Y;
                // 2.4.2 for all connected pin, add to a vector
                for(auto &Pin : NetPtr->get_pin_list()) {
                    auto PinCell = Pin.get_instance(); // 获取引脚所在的cell。ContestPin->IdbInstance
                    if(PinCell == CellPtr) continue;
                    int PinRow = PinCell->get_coordinate()->get_x();
                    int PinCol = PinCell->get_coordinate()->get_y();

                    // 真实坐标转为Gcell坐标，用myContestEvaluation::getGCellCoord会出问题。
                    int single_gcell_x_span = _data_manager->get_database()->get_single_gcell_x_span();
                    int single_gcell_y_span = _data_manager->get_database()->get_single_gcell_y_span();
                    PinRow = (PinRow > 0 ? PinRow - 1 : PinRow) / single_gcell_x_span;
                    PinCol = (PinCol > 0 ? PinCol - 1 : PinCol) / single_gcell_y_span;

                    X.push_back(PinRow);
                    Y.push_back(PinCol);
                }
                // 2.4.3 Simulating that the cell is moved to this candidate position
                X.push_back(P.first);
                Y.push_back(P.second);
                // 对于不在任何net里的cell，(X, Y)只有一个点，Flute无法计算线长，就会报错。加一个冗余点进去。
                X.push_back(P.first);
                Y.push_back(P.second);

                // 2.4.4 Call FLUTE
                // Flute要传入网格坐标。
                auto Wrapper = Flute::FluteWrapper::getInstance();
                auto FluteTree = Wrapper->runFlute(X, Y);

                // 2.4.5 Definition of Candidate_Region_Cost: FLUTE_Length * Net_NumPins^2 (Net with More Pin is more impactful)
                total_WL += FluteTree.getLength() * pow(NetPtr->get_pin_list().size(),2);
            }
            // 2.4.6 Record this candidate position's resulting total WL
            Grid_to_WL[P] = total_WL;
        }

        // 2.5 Sorting each Candidate Position by Candidate_Region_Cost (Smaller Cost is better)
        // std::sort(CandidatePos.begin(), CandidatePos.end(), [&Grid_to_WL](std::pair<int, int> a, std::pair<int, int> b) 
        // {return Grid_to_WL[a] < Grid_to_WL[b]; }); // 对应论文中图7(b)，可去掉。
        
        // 2.6 Remove Cell and its connected nets
        Route Route_tmp(_data_manager);
        int OldCellRow = CellPtr->get_coordinate()->get_x();
        int OldCellCol = CellPtr->get_coordinate()->get_y();

        // 真实坐标转为Gcell坐标，用myContestEvaluation::getGCellCoord会出问题。
        int single_gcell_x_span = _data_manager->get_database()->get_single_gcell_x_span();
        int single_gcell_y_span = _data_manager->get_database()->get_single_gcell_y_span();
        OldCellRow = (OldCellRow > 0 ? OldCellRow - 1 : OldCellRow) / single_gcell_x_span;
        OldCellCol = (OldCellCol > 0 ? OldCellCol - 1 : OldCellCol) / single_gcell_y_span;

        {
            for (auto NetPtr : _data_manager->get_database()->getRelativeNetsMap().at(CellPtr)) {
                Route_tmp.removeNet(NetPtr);
            }
            removeCell(CellPtr); // CellPtr对象还留着，只不过不占面积了。
        }

        // 2.7 Add back cell and reroute
        bool Success = false;
        for (auto P : CandidatePos) {
            if (add_and_route(CellPtr, P.first, P.second, false).second) {
                Success = true;
                break;
            }
        }

        // 2.8.1 Succes
        if (Success) {
            ++MoveCnt;
            std::cerr << "Number of cell movement: "<< MoveCnt << std::endl;
        // 2.8.2 If failed add back as original 
        } else {
            addCell(CellPtr, OldCellRow, OldCellCol);
            for (auto NetPtr : _data_manager->get_database()->getRelativeNetsMap().at(CellPtr)) {
                Route_tmp.addNet(NetPtr);
            }
        }

        // 2.9 Output the Cell Movement Count
        ++tryMoveCnt;
        if (tryMoveCnt % 100 == 0) {
            std::cerr << "Try to move cell number "<< tryMoveCnt << std::endl;
        }

        // 2.9 Check the Max Cell Movement Constraint
        if (MoveCnt == 100000 || tryMoveCnt == 2000) {
            std::cout << "MAX CELL MOVE\n";
            break;
        }

        // 2.10 Check Timer
        auto endTime = std::chrono::high_resolution_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::duration<float>>(endTime - startTime);
        if(duration.count() > 300000.0) break;
    }
}

// 其他方法的实现...

} // namespace ieda_contest